﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Project
{
    public class CollisionManager
    {
        protected List<CollisionObject> collisionObjects { get; set; }
        public CollisionMap map { get; protected set; }

        public void addCollisionObject(CollisionObject c)
        {
            collisionObjects.Add(c);
        }

        public void removeCollisionObject(CollisionObject c)
        {
            collisionObjects.Remove(c);
        }

        public CollisionManager(CollisionMap map)
        {
            collisionObjects = new List<CollisionObject>();
            this.map = map;
        }

        /// <summary>
        /// Returns true if c intersects with solid geometry in the scene.
        /// </summary>
        /// <param name="c"></param>
        /// <returns>True if there is a collision with a solid object, false otherwise.</returns>
        public bool collides(AABB box)
        {
            return map.collides(box);
        }

        /// <summary>
        /// Tests and handles interactions between c and other collision objects (both static map elements, and other dynamic collision objects).
        /// Collision callbacks are invoked for both c and other dynamic objects.
        /// NOTE, however, that for static map elements, only the map element's collision handler is invoked (and NOT the given collision object's handler).
        /// </summary>
        /// <param name="c"></param>
        public void interact(CollisionObject c, GameTime gt, GameState gs)
        {
            // handle collisions against the scenery
            map.interact(c, gt, gs);

            //check against other collisionObjects
            foreach (CollisionObject co in collisionObjects)
            {
                if (c.parent != co.parent)
                {
                    if (c.collisionBox.intersects(co.collisionBox))//there is a collision
                    {
                        CollisionData data1 = new CollisionData(c.parent, gt, gs);
                        co.handler(data1);

                        CollisionData data2 = new CollisionData(co.parent, gt, gs);
                        c.handler(data2);
                    }
                }
            }
        }
    }

    public class CollisionObject
    {
        public delegate void CollisionHandler(CollisionData data);

        public AABB collisionBox;
        public CollisionHandler handler;
        public GameEntity parent;

        public CollisionObject(AABB collisionBox, CollisionHandler handler, GameEntity parent)
        {
            this.collisionBox = collisionBox;
            this.handler = handler;
            this.parent = parent;
        }
    }

    public class CollisionData
    {
        public GameEntity entity;
        public GameTime gameTime;
        public GameState gameState;

        public CollisionData()
        {
        }
        public CollisionData(GameEntity entity, GameTime gt, GameState gs)
        {
            this.entity = entity;
            this.gameTime = gt;
            this.gameState = gs;
        }
    }

    public struct AABB
    {
        public Vector3 min;
        public Vector3 WorldMin
        {
            get
            {
                return min + position;
            }
        }
        public Vector3 max;
        public Vector3 WorldMax
        {
            get
            {
                return max + position;
            }
        }
        // min and max are relative to position
        public Vector3 position;

        public AABB(Vector3 min, Vector3 max, Vector3 position)
        {
            this.min = min;
            this.max = max;
            this.position = position;
        }

        public bool intersects(AABB other)
        {
            return (WorldMin.X < other.WorldMax.X && WorldMax.X > other.WorldMin.X &&
                WorldMin.Y < other.WorldMax.Y && WorldMax.Y > other.WorldMin.Y &&
                WorldMin.Z < other.WorldMax.Z && WorldMax.Z > other.WorldMin.Z
                );
        }
    }
}
